Skip to content

Make local server stop <name> idempotent (#255)#260

Merged
sdairs merged 1 commit into
mainfrom
issue-255-idempotent-stop
Jun 18, 2026
Merged

Make local server stop <name> idempotent (#255)#260
sdairs merged 1 commit into
mainfrom
issue-255-idempotent-stop

Conversation

@sdairs

@sdairs sdairs commented Jun 16, 2026

Copy link
Copy Markdown
Collaborator

Closes #255.

Problem

chctl local server stop <name> exits non-zero (ServerNotRunning) when the named server isn't running, forcing scripts to guard against the already-stopped case. stop-all is already idempotent (exits 0 with "No running servers"), so the two were inconsistent.

Approach

Rather than add the --none-ok flag from the issue, stop is now idempotent by default — the declarative paradigm used by docker stop / systemctl stop:

Command Before After
stop <running> stopped, exit 0 stopped, exit 0 (unchanged)
stop <stopped-but-exists> error, exit 1 already stopped, exit 0
stop <unknown-name> error, exit 1 not found, exit 1 (unchanged)

The unknown-name case still errors (ServerNotFound) so typos surface — we distinguish it from already-stopped via the on-disk data dir (project-scoped, same check remove uses).

This is a deliberate behavior change, agreed appropriate pre-1.0 and consistent with stop-all.

Changes

  • classify_stop(running, exists_on_disk) pure helper encodes the three outcomes; the handler stays thin. Unit-tested for all three cases.
  • ServerStopOutput gains already_stopped: bool, surfaced in human output ("Server 'x' is already stopped") and --json.
  • --global stop and Postgres stop are unchanged (out of scope; global is cross-project maintenance where the project-scoped data-dir check doesn't apply).
  • README + stop agent-help text document the idempotent behavior.

Testing

  • cargo build, cargo test -p clickhousectl (392 tests), cargo clippy --all-targets all clean.
  • New unit tests: classify_stop_* (3 outcomes), server_stop_already_stopped (display + JSON).
  • Manual smoke test: unknown name → exit 1; existing-but-stopped → exit 0 with already_stopped: true.

🤖 Generated with Claude Code


Note

Low Risk
Localized CLI behavior change for project-scoped ClickHouse stop only; running stops and unknown-name errors are unchanged aside from the already-stopped success path.

Overview
Project-scoped clickhousectl local server stop <name> now treats an existing but stopped server as success (exit 0) instead of failing with a not-running error, aligning with stop-all and typical docker stop / systemctl stop behavior.

The handler uses classify_stop(running, data_dir_exists) to choose kill, idempotent noop, or ServerNotFound for unknown names. ServerStopOutput adds already_stopped so CLI text says the server is already stopped and --json can tell a real stop from a noop. README and server stop agent help document this. --global stop and Postgres stop are unchanged.

Reviewed by Cursor Bugbot for commit 842ddb8. Bugbot is set up for automated code reviews on this repo. Configure here.

Stopping a server that exists on disk but is already stopped now exits 0
("Server 'x' is already stopped") instead of erroring with
ServerNotRunning. This matches docker/systemctl semantics and the
existing idempotent behavior of `stop-all`, so scripts no longer need to
guard against an already-stopped server.

An unknown server name (no data dir in the project) still returns
ServerNotFound (exit 1), so typos are caught. `--global` stop and the
Postgres stop command are unchanged.

The stop decision is extracted into a pure `classify_stop` helper and
unit-tested for all three outcomes; `ServerStopOutput` gains an
`already_stopped` flag surfaced in both human and `--json` output.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@sdairs sdairs requested a review from iskakaushik as a code owner June 16, 2026 09:46

@iskakaushik iskakaushik left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@sdairs sdairs merged commit 1cee02e into main Jun 18, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add flag to prevent error when stop or stop-all finds the server isn't already running

2 participants